package com.alex.springbootdemo.service.impl;

import com.alex.springbootdemo.common.Constants;
import com.alex.springbootdemo.common.ResultCode;
import com.alex.springbootdemo.controller.UmsAdminController;
import com.alex.springbootdemo.mapper.mbg.UmsAdminMapper;
import com.alex.springbootdemo.mapper.mbg.UmsAdminRoleRelationMapper;
import com.alex.springbootdemo.model.param.UserLoginParam;
import com.alex.springbootdemo.model.pojo.UmsAdmin;
import com.alex.springbootdemo.model.pojo.UmsPermission;
import com.alex.springbootdemo.model.pojo.UmsResource;
import com.alex.springbootdemo.model.vo.JWTToken;
import com.alex.springbootdemo.security.AdminUserDetails;
import com.alex.springbootdemo.security.JwtTokenUtil;
import com.alex.springbootdemo.service.UmsAdminService;
import com.alex.springbootdemo.service.UmsResourceService;
import com.alex.springbootdemo.utils.AppContext;
import com.alex.springbootdemo.utils.AssertUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.weekend.Weekend;

import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class UmsAdminServiceImpl implements UmsAdminService {
    private static final Logger LOGGER = LoggerFactory.getLogger(UmsAdminServiceImpl.class);
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Autowired
    private UmsAdminMapper adminMapper;
    @Autowired
    private UmsAdminRoleRelationMapper adminRoleRelationMapper;
    @Autowired
    private UmsResourceService umsResourceService;


    @Override
    @Cacheable(value = Constants.CACHE_ADMIN_INFO, key = "#username")
    public UmsAdmin getAdminByUsername(String username) {
        Weekend<UmsAdmin> weekend = Weekend.of(UmsAdmin.class);
        weekend.weekendCriteria()
                .andEqualTo(UmsAdmin::getUsername, username);
        UmsAdmin umsAdmin = adminMapper.selectOneByExample(weekend);
        AssertUtil.isNotNull(umsAdmin, ResultCode.USER_NOT_FOUND);
        return umsAdmin;
    }

    @Override
    public UmsAdmin register(UmsAdmin umsAdminParam) {
        UmsAdmin umsAdmin = new UmsAdmin();
        BeanUtils.copyProperties(umsAdminParam, umsAdmin);
        umsAdmin.setCreateTime(new Date());
        umsAdmin.setStatus(1);
        //查询是否有相同用户名的用户
        Weekend<UmsAdmin> weekend = Weekend.of(UmsAdmin.class);
        weekend.weekendCriteria()
                .andEqualTo(UmsAdmin::getUsername, umsAdminParam.getUsername());
        List<UmsAdmin> umsAdminList = adminMapper.selectByExample(weekend);
        AssertUtil.isNotEmpty(umsAdminList, ResultCode.USERNAME_IN_USE);
        //将密码进行加密操作
        String encodePassword = AppContext.getInstance(PasswordEncoder.class)
                .encode(umsAdmin.getPassword());
        umsAdmin.setPassword(encodePassword);
        adminMapper.insert(umsAdmin);
        return umsAdmin;
    }

    @Override
    public JWTToken login(UserLoginParam param) {
        UserDetails userDetails = AppContext.getInstance(UserDetailsService.class)
                .loadUserByUsername(param.getUsername());
        if (!AppContext.getInstance(PasswordEncoder.class)
                .matches(param.getPassword(), userDetails.getPassword())) {
            throw new BadCredentialsException("密码不正确");
        }
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        long tokenValidInSeconds = param.getRememberMe() ?
                Constants.TOKEN_VALIDITY_IN_SECONDS_FOR_REMEMBER_ME : Constants.TOKEN_VALIDITY_IN_SECONDS;
        String idToken = jwtTokenUtil.generateToken(userDetails, tokenValidInSeconds);
        String refreshToken = jwtTokenUtil.generateToken(userDetails, Constants.REFRESH_TOKEN_VALIDITY_IN_SECONDS);
        JWTToken jwtToken = new JWTToken();
        jwtToken.setIdToken(idToken)
                .setExpiresIn(tokenValidInSeconds)
                .setRefreshToken(refreshToken);
        return jwtToken;
    }


    @Override
    @Cacheable(value = Constants.CACHE_ADMIN_PERMISSION, key = "#adminId")
    public List<UmsPermission> getPermissionList(Long adminId) {
        return adminRoleRelationMapper.getPermissionListByUserId(adminId);
    }

    @Override
    public JWTToken refreshToken(UmsAdminController.RefreshTokenRequest param) {
        String refreshToken = param.getRefreshToken();
        Map<String, Object> map = jwtTokenUtil.refreshToken(refreshToken);
        JWTToken jwtToken = new JWTToken()
                .setIdToken((String) map.get("token"))
                .setExpiresIn((Long) map.get("expiresIn"))
                .setRefreshToken(refreshToken);
        return jwtToken;
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        UmsAdmin admin = getAdminByUsername(username);
        AssertUtil.isNotNull(admin, ResultCode.USER_NOT_FOUND);
        List<UmsResource> resourceList = umsResourceService.getResourceListByAdminId(admin.getId());
        return new AdminUserDetails(admin, resourceList);
    }
}
